﻿using MyCompany.MyProject.Common.Core;
using MyCompany.MyProject.Common.DB;
using MyCompany.MyProject.Model;
using MyCompany.MyProject.Model.Models;
using MyCompany.MyProject.Model.Models.Tenants;
using MyCompany.MyProject.Repository.UnitOfWork;
using Newtonsoft.Json;
using SqlSugar;
using System.Linq.Expressions;
using System.Reflection;

namespace MyCompany.MyProject.Repository.Base;

public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class, new()
{
    private readonly SqlSugarScope _dbBase;
    private readonly IUnitOfWorkManage _unitOfWork;

    public BaseRepository(IUnitOfWorkManage unitOfWork)
    {
        _unitOfWork = unitOfWork;
        _dbBase = unitOfWork.GetDbClient();
    }

    public ISqlSugarClient Db => _db;

    private ISqlSugarClient _db
    {
        get
        {
            ISqlSugarClient db = _dbBase;

            //修改使用 model备注字段作为切换数据库条件，使用sqlsugar TenantAttribute存放数据库ConnId
            //参考 https://www.donet5.com/Home/Doc?typeId=2246
            var tenantAttr = typeof(TEntity).GetCustomAttribute<TenantAttribute>();
            if (tenantAttr != null)
            {
                //统一处理 configId 小写
                db = _dbBase.GetConnectionScope(tenantAttr?.configId?.ToString()?.ToLower());
                return db;
            }
            // 多租户
            var mta = typeof(TEntity).GetCustomAttribute<MultiTenantAttribute>();
            if (mta is { TenantType: TenantTypeEnum.Db })
            {
                // 获取租户信息 租户信息可以提前缓存下来
                if (App.User is { TenantId: > 0 })
                {
                    var tenant = db.Queryable<SysTenant>().WithCache().Where(s => s.Id == App.User.TenantId).First();
                    if (tenant != null)
                    {
                        var iTenant = db.AsTenant();
                        if (!iTenant.IsAnyConnection(tenant.ConfigId))
                        {
                            iTenant.AddConnection(tenant.GetConnectionConfig());
                        }
                        return iTenant.GetConnectionScope(tenant.ConfigId);
                    }
                }
            }
            return db;
        }
    }

    /// <summary>
    /// 查询
    /// </summary>
    /// <param name="whereExpression"></param>
    /// <returns></returns>
    public async Task<List<TEntity>> Query(Expression<Func<TEntity, bool>>? whereExpression = null)
    {
        //await Task.CompletedTask;
        //var data = "[{\"Id\": 18,\"Name\":\"laozhang\"},{\"Id\": 16,\"Name\":\"老王\"}]";
        //return JsonConvert.DeserializeObject<List<TEntity>>(data) ?? new List<TEntity>();

        return await _db.Queryable<TEntity>().WhereIF(whereExpression != null, whereExpression).ToListAsync();
    }

    /// <summary>
    /// 新增
    /// </summary>
    /// <param name="entity"></param>
    /// <returns></returns>
    public async Task<long> Add(TEntity entity)
    {
        var insert = _db.Insertable<TEntity>(entity);
        return await insert.ExecuteReturnSnowflakeIdAsync();
    }

    /// <summary>
    /// 分表查询
    /// </summary>
    /// <param name="whereExpression"></param>
    /// <param name="orderField"></param>
    /// <returns></returns>
    public async Task<List<TEntity>> QuerySplit(Expression<Func<TEntity, bool>> whereExpression, string orderField = "")
    {
        return await _db.Queryable<TEntity>().SplitTable()
            .WhereIF(whereExpression != null, whereExpression)
            .OrderByIF(!string.IsNullOrEmpty(orderField), orderField)
            .ToListAsync();
    }

    /// <summary>
    /// 添加分表数据
    /// </summary>
    /// <param name="entity"></param>
    /// <returns></returns>
    public async Task<List<long>> AddSplit(TEntity entity)
    {
        var insert = _db.Insertable(entity).SplitTable();
        return await insert.ExecuteReturnSnowflakeIdListAsync();
    }

    public async Task<List<TResult>> QueryMuch<T, T2, T3, TResult>(Expression<Func<T, T2, T3, object[]>> joinExpression,
        Expression<Func<T, T2, T3, TResult>> selectExpression,
        Expression<Func<T, T2, T3, bool>> whereExpression = null) where T : class, new()
    {
        if (whereExpression == null)
        {
            return await _db.Queryable(joinExpression).Select(selectExpression).ToListAsync();
        }
        return await _db.Queryable(joinExpression).Where(whereExpression).Select(selectExpression).ToListAsync();
    }

    public async Task<List<TEntity>> QueryWithCache(Expression<Func<TEntity, bool>>? whereExpression = null)
    {
        return await _db.Queryable<TEntity>().WhereIF(whereExpression != null, whereExpression).WithCache().ToListAsync();
    }

    public async Task<TEntity> QueryById(object id)
    {
        return await _db.Queryable<TEntity>().In(id).WithCache().SingleAsync();
    }

    public async Task<PageModel<TEntity>> QueryPage(Expression<Func<TEntity, bool>>? whereExpression = null, int pageIndex = 1, int pageSize = 20, string? orderByFields = null)
    {
        RefAsync<int> totalCount = 0;
        var list = await _db.Queryable<TEntity>()
            .OrderByIF(!string.IsNullOrEmpty(orderByFields) , orderByFields )
            .WhereIF(whereExpression != null, whereExpression)
            .ToPageListAsync(pageIndex,pageSize,totalCount);

        return new PageModel<TEntity>(pageIndex,totalCount,pageSize,list);
    }

    public async Task<bool> DeleteByIds(object[] ids)
    {
        return await _db.Deleteable<TEntity>().In(ids).ExecuteCommandHasChangeAsync();
    }

    public async Task<List<long>> Add(List<TEntity> listEntity)
    {
        return await _db.Insertable(listEntity.ToArray()).ExecuteReturnSnowflakeIdListAsync();
    }


    public async Task<bool> Update(TEntity entity)
    {
        return await _db.Updateable(entity).ExecuteCommandHasChangeAsync();
    }

    public async Task<List<TEntity>> QueryByIDs(long[] lstIds)
    {
        return await _db.Queryable<TEntity>().In(lstIds).WithCache().ToListAsync();
    }

    public async Task<bool> Update(List<TEntity> model)
    {
        return await _db.Updateable(model).ExecuteCommandHasChangeAsync();
    }

    public async Task<TEntity> QueryById(object id, bool blnUseCache = false)
    {
        return await _db.Queryable<TEntity>().WithCacheIF(blnUseCache,10).In(id).SingleAsync();
    }
}
